# SPDX-License-Identifier: MulanPSL-2.0+

import logging
import os
import uuid
import time
import threading
from logging.handlers import RotatingFileHandler
from typing import Dict, Any
import json
import traceback
from datetime import datetime

def generate_thread_unique_session_id() -> str:
    """生成基于线程ID + 时间戳的唯一会话标识符"""
    thread_id = threading.current_thread().ident
    timestamp = int(time.time() * 1000000)  # 微秒级时间戳
    return f"{thread_id % 0xFFFFFFFF:08x}_{timestamp % 0xFFFFFF:06x}"

class StructuredLogger:
    """统一的结构化日志记录器 - 支持每个会话独立日志"""

    _instances = {}  # 改为字典存储多个实例

    def __new__(cls, session_id: str = None, *args, **kwargs):
        if session_id is None:
            session_id = generate_thread_unique_session_id()

        if session_id not in cls._instances:
            instance = super().__new__(cls)
            cls._instances[session_id] = instance
        return cls._instances[session_id]

    def __init__(self, session_id: str = None, log_dir: str = None, name: str = 'py_bisect'):
        if session_id is None:
            session_id = generate_thread_unique_session_id()

        # 避免重复初始化
        if hasattr(self, '_initialized') and getattr(self, '_session_id', None) == session_id:
            # 如果已经初始化但是需要添加文件handler
            if log_dir and not getattr(self, '_file_handler_set', False):
                self._setup_file_handlers(log_dir)
                self._file_handler_set = True
            return

        self.session_id = session_id
        self._session_id = session_id  # 用于检查重复初始化
        self.logger = logging.getLogger(f'{name}_{session_id}')
        self.logger.propagate = False  # Prevent log propagation
        self.logger.handlers = []      # Clear existing handlers

        self.start_time = time.time()
        self.context: Dict[str, Any] = {
            'session_id': self.session_id,
            'start_time': datetime.fromtimestamp(self.start_time).isoformat()
        }

        if log_dir:
            self._setup_file_handlers(log_dir)
            self._file_handler_set = True
        else:
            self._file_handler_set = False

        self._setup_console_handler()
        self._initialized = True

    def _setup_file_handlers(self, log_dir: str):
        """配置文件处理器 - 每个会话独立的日志文件"""
        try:
            os.makedirs(log_dir, exist_ok=True)
        except Exception:
            return

        # 使用固定的日志文件名
        log_filename = 'py_bisect.log'
        log_filepath = os.path.join(log_dir, log_filename)

        try:
            # 检查文件权限
            if os.path.exists(log_filepath):
                if not os.access(log_filepath, os.W_OK):
                    return
            else:
                # 测试是否可以创建文件
                try:
                    with open(log_filepath, 'a') as test_file:
                        pass
                except Exception:
                    return

            combined_handler = RotatingFileHandler(
                log_filepath,
                maxBytes=10*1024*1024,  # 10MB
                backupCount=5,
                encoding='utf-8'
            )

            # 统一日志格式，包含会话ID
            show_session_id = os.environ.get('LKP_BISECT_LOG_SESSION_ID', 'true').lower() not in ['false', '0']
            log_format = (
                f'%(asctime)s.%(msecs)03d [%(levelname)s] [Session:{self.session_id}] %(filename)s:%(lineno)d - %(message)s'
                if show_session_id
                else '%(asctime)s.%(msecs)03d [%(levelname)s] %(filename)s:%(lineno)d - %(message)s'
            )
            formatter = logging.Formatter(log_format, '%Y-%m-%d %H:%M:%S')
            combined_handler.setFormatter(formatter)
            combined_handler.setLevel(logging.DEBUG)

            # 清理旧的文件handler后添加新handler
            old_handlers = [h for h in self.logger.handlers if isinstance(h, RotatingFileHandler)]
            for h in old_handlers:
                self.logger.removeHandler(h)

            self.logger.addHandler(combined_handler)
            # 立即flush确保日志写入
            combined_handler.flush()

        except Exception:
            import traceback
            traceback.print_exc()

    def _setup_console_handler(self):
        """配置控制台处理器"""
        console = logging.StreamHandler()

        # 从环境变量读取日志级别，默认为INFO
        log_level = os.environ.get('LOG_LEVEL', 'INFO').upper()
        level_map = {
            'DEBUG': logging.DEBUG,
            'INFO': logging.INFO,
            'WARNING': logging.WARNING,
            'ERROR': logging.ERROR,
            'CRITICAL': logging.CRITICAL
        }
        console_level = level_map.get(log_level, logging.INFO)

        console.setLevel(console_level)
        show_session_id = os.environ.get('LKP_BISECT_LOG_SESSION_ID', 'true').lower() not in ['false', '0']
        log_format = (
            f'[py_bisect:{self.session_id}] %(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
            if show_session_id
            else '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
        )
        formatter = logging.Formatter(log_format, '%Y-%m-%d %H:%M:%S')
        console.setFormatter(formatter)
        if not any(isinstance(h, logging.StreamHandler) for h in self.logger.handlers):
            self.logger.addHandler(console)
        self.logger.setLevel(logging.DEBUG)

    def setLevel(self, level):
        """Set the logging level for the underlying logger."""
        self.logger.setLevel(level)

    def configure(self, log_dir: str = None, level: int = logging.INFO):
        """动态配置日志"""
        if log_dir and not getattr(self, '_file_handler_set', False):
            self._setup_file_handlers(log_dir)
            self._file_handler_set = True
        self.logger.setLevel(level)

    def log(self, level: str, message: str, *args, **kwargs):
        """统一日志记录方法，支持格式化字符串和结构化日志"""
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'level': level.upper(),
            'message': message % args if args else message,
            **self.context,
            **kwargs
        }

        # 写入JSON日志
        json_handler = next(
            (h for h in self.logger.handlers
             if isinstance(h, RotatingFileHandler) and 'structured' in h.baseFilename),
            None
        )
        if json_handler:
            try:
                json_handler.stream.write(json.dumps(log_entry) + '\n')
            except Exception as e:
                pass

        # 调用标准日志方法，添加stacklevel参数以获取正确的调用位置
        log_method = getattr(self.logger, level)
        if args:
            log_method(message, *args, extra=kwargs, stacklevel=3)
        else:
            log_method(message, extra=kwargs, stacklevel=3)

        # 强制flush所有handler确保日志立即写入
        for handler in self.logger.handlers:
            handler.flush()

    def debug(self, message: str, *args, **kwargs):
        self.log('debug', message, *args, **kwargs)

    def info(self, message: str, *args, **kwargs):
        self.log('info', message, *args, **kwargs)

    def warning(self, message: str, *args, **kwargs):
        self.log('warning', message, *args, **kwargs)

    def error(self, message: str, *args, **kwargs):
        self.log('error', message, *args, **kwargs)

    def exception(self, message: str, **kwargs):
        kwargs['exception'] = traceback.format_exc()
        self.log('error', message, **kwargs)

# 创建日志实例的工厂函数
def get_logger(session_id: str = None, log_dir: str = None) -> StructuredLogger:
    """获取指定会话的日志实例"""
    return StructuredLogger(session_id=session_id, log_dir=log_dir)

# 默认全局日志实例（向后兼容）- 延迟初始化
_global_logger = None
def get_global_logger():
    global _global_logger
    if _global_logger is None:
        _global_logger = StructuredLogger()
    return _global_logger

# 向后兼容的全局logger属性
class GlobalLoggerProxy:
    def __getattr__(self, name):
        # 直接调用全局logger的方法或属性
        global_logger = get_global_logger()
        if hasattr(global_logger, name):
            return getattr(global_logger, name)
        # 如果是logger对象的属性，也尝试从其内部logger获取
        if hasattr(global_logger.logger, name):
            return getattr(global_logger.logger, name)
        raise AttributeError(f"'{type(global_logger).__name__}' object has no attribute '{name}'")

logger = GlobalLoggerProxy()
